﻿Imports System
Imports System.Collections
Imports System.Diagnostics
Imports System.Workflow.ComponentModel
Imports System.Workflow.Runtime.Hosting

Namespace System.Workflow.Runtime
    <Serializable> _
    Public Class TimerEventSubscriptionCollection
        Implements ICollection, IEnumerable
        ' Methods
        Friend Sub New(ByVal executor As WorkflowExecutor)
            Me.executor = executor
            Me.instanceId = executor.InstanceId
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Created", New Object() { Me.instanceId })
            AddHandler Me.queue.FirstElementChanged, New EventHandler([Of] KeyedPriorityQueueHeadChangedEventArgs([Of] TimerEventSubscription))(AddressOf Me.OnFirstElementChanged)
        End Sub

        Public Sub Add(ByVal item As TimerEventSubscription)
            If (item Is Nothing) Then
                Throw New ArgumentNullException("item")
            End If
            Me.Enqueue(item)
        End Sub

        Public Sub CopyTo(ByVal array As Array, ByVal index As Integer)
            Dim subscriptionArray As TimerEventSubscription() = Nothing
            SyncLock Me.locker
                subscriptionArray = New TimerEventSubscription(Me.queue.Count  - 1) {}
                Me.queue.Values.CopyTo(subscriptionArray, 0)
            End SyncLock
            If (Not subscriptionArray Is Nothing) Then
                subscriptionArray.CopyTo(array, index)
            End If
        End Sub

        Friend Function Dequeue() As TimerEventSubscription
            SyncLock Me.locker
                Dim subscription As TimerEventSubscription = Me.queue.Dequeue
                If (Not subscription Is Nothing) Then
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Dequeue Timer {1} for {2} ", New Object() { Me.instanceId, subscription.SubscriptionId, subscription.ExpiresAt })
                End If
                Return subscription
            End SyncLock
        End Function

        Friend Sub Enqueue(ByVal timerEventSubscription As TimerEventSubscription)
            SyncLock Me.locker
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Enqueue Timer {1} for {2} ", New Object() { Me.instanceId, timerEventSubscription.SubscriptionId, timerEventSubscription.ExpiresAt })
                Me.queue.Enqueue(timerEventSubscription.SubscriptionId, timerEventSubscription, timerEventSubscription.ExpiresAt)
            End SyncLock
        End Sub

        Public Function GetEnumerator() As IEnumerator
            Return Me.queue.Values.GetEnumerator
        End Function

        Private Sub OnFirstElementChanged(ByVal source As Object, ByVal e As KeyedPriorityQueueHeadChangedEventArgs([Of] TimerEventSubscription))
            SyncLock Me.locker
                Dim service As WorkflowSchedulerService = Me.Runtime.GetService([Of] WorkflowSchedulerService)
                If ((Not e.NewFirstElement Is Nothing) AndAlso (Not Me.executor Is Nothing)) Then
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Schedule Timer {1} for {2} ", New Object() { Me.instanceId, e.NewFirstElement.SubscriptionId, e.NewFirstElement.ExpiresAt })
                    service.Schedule(New WaitCallback(AddressOf Me.executor.WorkflowInstance.ProcessTimers), e.NewFirstElement.WorkflowInstanceId, e.NewFirstElement.ExpiresAt, e.NewFirstElement.SubscriptionId)
                End If
                If (Not e.OldFirstElement Is Nothing) Then
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Unschedule Timer {1} for {2} ", New Object() { Me.instanceId, e.OldFirstElement.SubscriptionId, e.OldFirstElement.ExpiresAt })
                    service.Cancel(e.OldFirstElement.SubscriptionId)
                End If
            End SyncLock
        End Sub

        Public Function Peek() As TimerEventSubscription
            SyncLock Me.locker
                Return Me.queue.Peek
            End SyncLock
        End Function

        Public Sub Remove(ByVal timerSubscriptionId As Guid)
            SyncLock Me.locker
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Remove Timer {1}", New Object() { Me.instanceId, timerSubscriptionId })
                Me.queue.Remove(timerSubscriptionId)
            End SyncLock
        End Sub

        Public Sub Remove(ByVal item As TimerEventSubscription)
            If (item Is Nothing) Then
                Throw New ArgumentNullException("item")
            End If
            Me.Remove(item.SubscriptionId)
        End Sub

        Friend Sub ResumeDelivery()
            SyncLock Me.locker
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Resume", New Object() { Me.instanceId })
                Dim service As WorkflowSchedulerService = Me.Runtime.GetService([Of] WorkflowSchedulerService)
                Dim subscription As TimerEventSubscription = Me.queue.Peek
                If (Not subscription Is Nothing) Then
                    service.Schedule(New WaitCallback(AddressOf Me.executor.WorkflowInstance.ProcessTimers), subscription.WorkflowInstanceId, subscription.ExpiresAt, subscription.SubscriptionId)
                End If
            End SyncLock
        End Sub

        Friend Sub SuspendDelivery()
            SyncLock Me.locker
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TimerEventSubscriptionQueue: {0} Suspend", New Object() { Me.instanceId })
                Dim service As WorkflowSchedulerService = Me.Runtime.GetService([Of] WorkflowSchedulerService)
                Dim subscription As TimerEventSubscription = Me.queue.Peek
                If (Not subscription Is Nothing) Then
                    service.Cancel(subscription.SubscriptionId)
                End If
            End SyncLock
        End Sub


        ' Properties
        Public ReadOnly Property Count As Integer
            Get
                Return Me.queue.Count
            End Get
        End Property

        Friend Property Executor As WorkflowExecutor
            Get
                Return Me.executor
            End Get
            Set(ByVal value As WorkflowExecutor)
                Me.executor = value
            End Set
        End Property

        Public ReadOnly Property IsSynchronized As Boolean
            Get
                Return True
            End Get
        End Property

        Friend ReadOnly Property Runtime As WorkflowRuntime
            Get
                Return Me.executor.WorkflowRuntime
            End Get
        End Property

        Public ReadOnly Property SyncRoot As Object
            Get
                Return Me.locker
            End Get
        End Property


        ' Fields
        <NonSerialized> _
        Private executor As WorkflowExecutor
        Private instanceId As Guid
        Private locker As Object = New Object
        Private queue As KeyedPriorityQueue([Of] Guid, TimerEventSubscription, DateTime) = New KeyedPriorityQueue([Of] Guid, TimerEventSubscription, DateTime)
        Private suspended As Boolean
        Public Shared ReadOnly TimerCollectionProperty As DependencyProperty = DependencyProperty.RegisterAttached("TimerCollection", GetType(TimerEventSubscriptionCollection), GetType(TimerEventSubscriptionCollection))
    End Class
End Namespace

